class Compiler {
  constructor (vm) {
    this.el = vm.$el
    this.vm = vm
    this.compile(this.el)
  }
  // 编译模板
  compile (el) {
    let childNodes = el.childNodes
    Array.from(childNodes).forEach(node => {
      // 处理文本节点
      if (this.isTextNode(node)) {
        this.compileText(node)
      } else if (this.isElementNode(node)) {
        // 处理元素节点
        this.compileElement(node)
      }

      if (node.childNodes && node.childNodes.length) {
        this.compile(node)
      }
    })
  }
  // 编译元素节点，处理指令
  compileElement (node) {
    Array.from(node.attributes).forEach(attr => {
      // 判断是否是指令
      let attrName = attr.name
      if (this.isDirective(attrName)) {
        // v-text
        let attrParamsIndex = this.getDirectiveParams(attrName)
        let attrParams = (attrParamsIndex !== -1) ? attrName.substr(attrParamsIndex + 1) : undefined
        // attrName = attrName.substr(2)    // 应该取-和:之间,冒号和-要去掉, substr方法未在ecmascipt标准化
        attrName = (attrParamsIndex !== -1) ? attrName.slice(2, attrParamsIndex) : attrName.slice(2)
        let key = attr.value
        this.update(node, key, attrName, attrParams)
      }
    })
  }

  update (node, key, attrName, attrParams) {
    let updateFn = this[attrName + 'Updater']
    updateFn && updateFn.call(this, node, this.vm[key], key, attrParams)
  }
  // v-text
  textUpdater (node, value, key) {
    this.removeDirective(node)
    node.textContent = value
    new Watcher(this.vm, key, (newValue) => {
      node.textContent = newValue
    })
  }
  // v-model
  modelUpdater (node, value, key) {
    this.removeDirective(node)
    node.value = value
    new Watcher(this.vm, key, (newValue) => {
      node.value = newValue
    })
    // 双向绑定
    node.addEventListener('input', () => {
      this.vm[key] = node.value
    })
  }

  // v-html

  htmlUpdater (node, value, key) {
    this.removeDirective(node)
    // 先移除所有直接子节点进行覆盖
    let childNodes = node.childNodes
    for(let i = 0;i < childNodes.length;i++) {
      node.removeChild(childNodes[i])
    }
    node.innerHTML = value
    new Watcher(this.vm, key, newValue => {
      for(let i = 0;i < childNodes.length;i++) {
        node.removeChild(childNodes[i])
      }
      node.innerHTML = newValue
    })
  }

  // v-on
  onUpdater (node, value, key, attrParams) {
    // 此处methods中的方法没有注入到实例中,value不能获取
    let handler = this.vm.$options.methods[key]
    node.addEventListener(attrParams, (e) => {
      console.log(e, arguments)
      handler()
    })

    // TODO:  此处的vue模板中事件处理函数暂时不能传参
  }

  // 移除模板中指令
  removeDirective (node) {
    Array.from(node.attributes).forEach(attr => {
      if(attr.name.indexOf('v-') !== -1) {
        node.removeAttribute(attr.name)
      }
    })
  }

  compileText (node) {
    let reg = /\{\{(.+?)\}\}/
    let value = node.textContent
    if (reg.test(value)) {
      let key = RegExp.$1.trim()
      node.textContent = value.replace(reg, this.vm[key])
      new Watcher(this.vm, key, (newValue) => {
        node.textContent = newValue
      })
    }
  }
  isDirective (attrName) {
    return attrName.startsWith('v-')
  }
  // 判断指令中是否有参数,冒号后的
  getDirectiveParams (attrName) {
    return attrName.indexOf(':')
  }
  isTextNode (node) {
    return node.nodeType === 3
  }
  isElementNode (node) {
    return node.nodeType === 1
  }
}
